local ngx = ngx
local type = type
local require = require
local table = table
local debug = debug
local traceback = debug.traceback
local tostring = table.tostring
local loadstring = table.loadstring

local log = require("modules.commhm.log")
local utils = require("modules.commhm.utils")
local time = require("modules.commhm.time")
local ssdbClient = require("modules.commhm.dc.ssdbDcClient")

local FOO = {
    _VERSION = "0.0.1",
    READ_ONLY = true,
    NO_CACHE = true,
    RESULT_MAX_LEN = 1024 * 100, -- 返回值最大长度
    COMM_DB = "comm",

    -- KEYS按部门分组
    department = {
        -- 业务部门相关keys
        business = require("modules.business_common.department.business_ssdb"),
        -- 业务中台相关keys
        platform = require("modules.business_common.department.platform_ssdb"),
    }
}

--ssdb key 严禁在代码逻辑内进行修改
FOO.KEYS = FOO.department.business -- 默认的是业务部门的KEYS

FOO.is_ssdb_key = function(key)
    return FOO.KEYS.NONE < key and FOO.KEYS.INVALID > key
end

FOO.init = function(name, cfg, keys)
    if keys then FOO.KEYS = keys end
    if not FOO.client and cfg then
        FOO.client = ssdbClient:new(name, cfg.server, cfg.select)
    end
end


FOO.fini = function()
    -- 防止无限循环 只调一次
    if ngx.ctx.has_final_called then
        return
    end

    if FOO.client then
        FOO.client:final() -- 远程强制解锁
    end
    ngx.ctx.__READ_ONLY__ = 0
    ngx.ctx.has_final_called = true
end

-- !@ TODO:是否应该从业务层传入收尾函数方便其他模块的收尾
FOO.panic = function(txt)
    FOO.fini()
    log.error(txt)
    time.print_cost_all(txt)
    ngx.exit(0)
end

FOO.select_ssdb = function(uin)
    if uin == "" or uin == FOO.COMM_DB then
        return FOO.COMM_DB
    end

    return uin % 10
end

-- !@ warn : 读取异常时该函数会panic中止当前http请求，避免出现后续业务异常数据
FOO.private_hash_get_table_ssdb = function(db, key, hash, read_only)
    read_only = read_only == FOO.READ_ONLY and 1 or 0

    if not FOO.is_ssdb_key(hash) then
        return FOO.panic("invalid ssdb key" .. hash)
    end

    local result = FOO.client:read(db, key, hash, FOO.panic, 0, read_only)
    if result == ngx.null then
        log.debug("get null value:" .. utils.to_str({ssdb = db, key = key, hash = hash}))
        return {}
    end

    -- 数据过长
    if #result > FOO.RESULT_MAX_LEN then
        log.error("read much" .. utils.to_str({ssdb = db, key = key, hash = hash, len = #result}))
    end

    -- 读出来的格式不对
    local result_table = loadstring(result)
    if type(result_table) ~= "table" then
        FOO.client:clean_shared_cache_ssdb(db, key, hash)
        return FOO.panic("return not table" .. utils.to_str({error = result, ssdb = db, key = key, hash = hash}))
    end

    -- 读出来的数据校验失败
    if not FOO.client:private_check_get_key(db, key, hash, result_table) then
        FOO.client:clean_shared_cache_ssdb(db, key, hash)
        return FOO.panic("check key fail" .. utils.to_str({ssdb = db, key = key, hash = hash, trace = traceback()}))
    end

    return result_table
end

-- !@ 写入失败交由业务层处理后续收尾
FOO.private_hash_set_table_ssdb = function(db, key, hash, value)
    -- 格式不对
    if not value or type(value) ~= "table" then
        log.error("value not table")
        return false
    end

    -- 校验失败
    if not FOO.client:private_check_set_key(db, key, hash, value) then
        log.error("check key fail")
        return false
    end

    return "OK" == FOO.client:save(db, key, hash, tostring(value)) and true or false
end

-- !@ 读取数据
-- ret :  1. 空table   - 无数据，新记录
--        2. 非空table - 成功读取
FOO.GET = function(uin, key, read_only)
    return FOO.private_hash_get_table_ssdb(FOO.select_ssdb(uin), uin, key, read_only)
end

-- !@ 存储数据
-- ret : true  - 写入成功
--       false - 写入失败
FOO.SET = function(uin, key, value)
    return FOO.private_hash_set_table_ssdb(FOO.select_ssdb(uin), uin, key, value)
end

return FOO
